`timescale 1ns / 1ps

// Single Port RAM, Write-First Mode
module zq_spram
#(
    parameter ADDR_WIDTH = 12,
    parameter DATA_WIDTH = 320,
    parameter DEPTH = 3072,              // ADDR_WIDTH >= $clog2(DEPTH)
    parameter LATENCY = 2,
    parameter RAMTYPE = "block",         // block or distributed
    parameter RAMFILE = "L9_PW.mem"
)
(
    input   clk,
    input   ena,
    input   wea,
    
    input   [DATA_WIDTH-1: 0]    din,
    input   [ADDR_WIDTH-1: 0]    addr,
    output  [DATA_WIDTH-1: 0]    dout
);

(* ram_style = RAMTYPE *)
reg  [DATA_WIDTH-1: 0]   ram [DEPTH-1: 0];

reg  [DATA_WIDTH-1: 0] o_reg [LATENCY-1: 0];

assign dout = o_reg[LATENCY-1];

always @(posedge clk)
begin
    if (ena)
    begin
        if (wea) begin
            ram[addr] <= din;
            o_reg[0] <= din;
        end
        else begin
            o_reg[0] <= ram[addr];
        end
    end
end

genvar i;
generate
    for (i = 0; i < LATENCY - 1; i = i + 1)
    begin
        always @(posedge clk)
        begin
            if (ena)
                o_reg[i + 1] <= o_reg[i];
        end
    end
endgenerate

generate
    if (RAMFILE != "")
    begin
        initial $readmemh(RAMFILE, ram, 0, DEPTH-1);
    end
endgenerate

endmodule


// Single Port ROM
module zq_sprom
#(
    parameter ADDR_WIDTH = 12,
    parameter DATA_WIDTH = 320,
    parameter DEPTH = 3072,              // ADDR_WIDTH >= $clog2(DEPTH)
    parameter LATENCY = 1,
    parameter ROMTYPE = "block",         // block or distributed
    parameter ROMFILE = "L9_PW.mem"
)
(
    input   clk,
    input   ena,
    
    input   [ADDR_WIDTH-1: 0]    addr,
    output  [DATA_WIDTH-1: 0]    dout
);

(* ram_style = ROMTYPE *)
reg  [DATA_WIDTH-1: 0]   ram [DEPTH-1: 0];

reg  [DATA_WIDTH-1: 0] o_reg [LATENCY-1: 0];

assign dout = o_reg[LATENCY-1];

always @(posedge clk)
begin
    if (ena)
        o_reg[0] <= ram[addr];
end

genvar i;
generate
    for (i = 0; i < LATENCY - 1; i = i + 1)
    begin
        always @(posedge clk)
        begin
            if (ena)
                o_reg[i + 1] <= o_reg[i];
        end
    end
endgenerate

initial
begin
    $readmemh(ROMFILE, ram, 0, DEPTH-1);
end

endmodule
